#include <WiFi.h>
#include <PubSubClient.h>
#include "RX8010.h"
#include "am2120.h"
#include <TimeLib.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#ifdef ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
// #define ESP32
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager

#define AM2120_SDA 33
#define ADC 34
#define IRQ1 35
#define IRQ2 32
#define E33_Enable 25
#define LINK 26
#define NET 27
#define WORK 12
#define LED 15
#define IR_OUT 13
#define IR 14
#define SLEEP_ENABLE 1
#define uS_TO_S_FACTOR 1000000

//
RTC_DATA_ATTR int bootCount = 0, UPDATE_F = 300;
RTC_DATA_ATTR char fileName[35];
RTC_DATA_ATTR char connectMode;
RTC_DATA_ATTR char SSID[10];
RTC_DATA_ATTR char PassWd[20];
unsigned long lastTime = 0;
dateTime dt;
bool tfStatus = 0;

float temp;
float humidity;
double vol;
char msg[200];

// 模式可选W：wifi或者G：上网模块
RTC_DATA_ATTR char mqtt_server[40];
RTC_DATA_ATTR char mqttUser[10];
RTC_DATA_ATTR char mqttPassWd[20];
RTC_DATA_ATTR int port = 1883;
RTC_DATA_ATTR char mqttID[20];
WiFiClient espClient;
WiFiUDP ntpUDP;
WiFiManager wm;
NTPClient timeClient(ntpUDP, "ntp.aliyun.com", 8 * 3600, 60000);
PubSubClient client(espClient);
bool wifiConnect()
{

    bool res;
    uint8_t macAddr[6];
    WiFi.macAddress(macAddr);
    char SSID[10];
    int i = 0;
    sprintf(SSID, "AP-%X%X", macAddr[4], macAddr[5]);

    wm.setTimeout(180);
    wm.setConnectTimeout(10);
    res = wm.autoConnect(SSID);
    WiFi.hostname("test"); // 连接后再设置host
    if (!res)
    {
        Serial.println("Failed to connect");
        return 0;
    }
    else
    {
        Serial.println("WiFi connected");
        Serial.print("IP address: ");
        Serial.println(WiFi.localIP());
        return 1;
    }
}
int print_wakeup_reason()
{
    int reason = 0;
    bool reasonPrint = 0;
    esp_sleep_wakeup_cause_t wakeup_reason;

    wakeup_reason = esp_sleep_get_wakeup_cause();

    switch (wakeup_reason)
    {
    case ESP_SLEEP_WAKEUP_EXT0:
        if (reasonPrint)
            Serial.println("使用 RTC_IO 的外部信号引起的唤醒");
        reason = 1;
        break;
    case ESP_SLEEP_WAKEUP_EXT1:
        if (reasonPrint)
            Serial.println("使用 RTC_CNTL 的外部信号引起的唤醒");
        reason = 2;
        break;
    case ESP_SLEEP_WAKEUP_TIMER:
        if (reasonPrint)
            Serial.println("定时器引起的唤醒");
        reason = 3;
        break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
        if (reasonPrint)
            Serial.println("触摸板引起的唤醒");
        reason = 4;
        break;
    case ESP_SLEEP_WAKEUP_ULP:
        if (reasonPrint)
            Serial.println("ULP 程序引起的唤醒");
        reason = 5;
        break;
    default:
        if (reasonPrint)
            Serial.printf("唤醒不是由深度睡眠引起的: %d\n", wakeup_reason);
        break;
    }
    delay(500);
    return reason;
}
void initMQTT()
{
    uint8_t macAddr[6];
    WiFi.macAddress(macAddr);
    sprintf(mqttID, "MQTT-%X%X%X", macAddr[3], macAddr[4], macAddr[5]);
    client.setServer(mqtt_server, port);
    client.setCallback(callback);
    reconnect();
}
void subScribe()
{
    client.subscribe("E33Set");
}
void callback(char *topic, byte *payload, unsigned int length)
{
    char msg[50];
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++)
    {
        Serial.print((char)payload[i]);
        msg[i] = (char)payload[i];
    }

    Serial.println();
}
void reconnect()
{
    while (!client.connected())
    {
        Serial.print("Attempting MQTT connection...");
        if (client.connect(mqttID, mqttUser, mqttPassWd))
        {
            Serial.println("connected");
            subScribe();
        }
        else
        {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}
bool E33VStart()
{
    int i, E33v_status = 1;
    for (i = 0; i < 3; i++)
    {
        Serial.printf("Power on E33V..%d\n", i);
        delay(500);
        digitalWrite(E33_Enable, 1);
        delay(500);
        lastTime = millis();
        Serial.print("Start net connection");
        while (!digitalRead(NET))
        {
            Serial.print(".");
            delay(1000);
            if (millis() - lastTime > 50000)
            {
                digitalWrite(E33_Enable, 0);
                Serial.println("Failed");
                E33v_status = -1;
                break;
            }
        }
        if (E33v_status != 1)
            continue;
        Serial.println("OK");
        lastTime = millis();
        Serial.print("Connect to MQTT server");
        while (!digitalRead(LINK))
        {
            Serial.print(".");
            delay(1000);
            if (millis() - lastTime > 100000)
            {
                digitalWrite(E33_Enable, 0);
                Serial.println("Failed");
                E33v_status = -2;
                break;
            }
        }
        if (E33v_status != 1)
            continue;
        Serial.println("OK");
        break;
    }
    if (E33v_status != 1)
        return 0;
    else
        return 1;
}
void appendFile(fs::FS &fs, const char *path, const char *message)
{
    Serial.printf("Appending to file: %s\t", path);

    File file = fs.open(path, FILE_APPEND);
    /* if (!file)
     {
         Serial.println("Failed!");
         return;
     }*/
    if (file.print(message))
    {
        Serial.println("OK");
    }
    else
    {
        Serial.println("Failed");
    }
    file.close();
}

void sdInit()
{
    char msg[] = "日期,时间,温度,湿度,电压,更新\n";
    if (tfStatus == 1)
    {
        sprintf(fileName, "/%02d%02d%02d_%02d%02d%02d.csv", dt.year, dt.month, dt.date, dt.hour, dt.minute, dt.second);
        /*  sprintf(msg, "日期,时间,温度,湿度,电压,更新\n",
                 dt.year, dt.month, dt.date, dt.hour, dt.minute, dt.second, temp, humidity, vol, bootCount); */
        Serial.printf("File created: %s\n", fileName);
        appendFile(SD, fileName, msg);
        //  appendFile(SD, "/Record.csv", msg);
    }
}
void getSetting()
{
    char msga[200];

    int i = 0;
    File file = SD.open("/set.ini");
    if (!file)
    {
        Serial.println("Failed to open file for reading");
    }

    while (file.available())
    {
        msga[i] = file.read();
        if (msga[i] == -1 | i >= 500)
        {
            msga[i] = '\0';
            break;
        }
        i++;
    }
    file.close();
    Serial.println(msga);
    sscanf(msga, "%d,%c,%[^,],%[^,],%[^,],%d", &UPDATE_F, &connectMode, mqtt_server, mqttUser, mqttPassWd, &port);

    Serial.printf("Update cycle set to %ds\n", UPDATE_F);
}

void tfLogger()
{
    char msg[200];
    if (tfStatus == 1)
    {
        //    sprintf(fileName, "/Record-%02d%02d%02d.csv", dt.year, dt.month, dt.date);
        sprintf(msg, "%02d-%02d-%02d,%02d:%02d:%02d,%.1f,%.1f,%.2f,%d\n",
                dt.year, dt.month, dt.date, dt.hour, dt.minute, dt.second, temp, humidity, vol, bootCount);
        appendFile(SD, fileName, msg);
        // appendFile(SD, "/Record.csv", msg);
    }
    delay(1000);
}
dateTime timeSync()
{
    dateTime dt;
    unsigned long epochTime;
    timeClient.update();
    epochTime = timeClient.getEpochTime();
    dt.year = year(epochTime);
    dt.month = month(epochTime);
    dt.date = day(epochTime);
    dt.day = weekday(epochTime);
    dt.hour = hour(epochTime);
    dt.minute = minute(epochTime);
    dt.second = second(epochTime);
    return dt;
}
void initRX8010(int updateTime)
{
    String msg;

    resetRX8010(); // 先复位设置
    Serial.print("1\t");
    setFCT(IRQ1, updateTime, 2); // 设置5分钟唤醒一次
    Serial.print("2\t");
    if (WiFi.status() == WL_CONNECTED)
    {
        //    timeClient.begin();
        Serial.print("3\t");
        dt = timeSync();
    }
    else
    {
        Serial2.print("+++"); // 获取EV33模块的时间并设置rx80100
        delay(50);
        if (Serial2.available())
        {
            msg = Serial2.readString();
            dt = timeSync();
        }
        Serial2.print("AT+TIMESTAMP");
        delay(50);
        if (Serial2.available())
        {
            msg = Serial2.readStringUntil('\n');
            msg = Serial2.readStringUntil('\n');
        }
        sscanf(msg.c_str(), "+TIMESTAMP: %d", &lastTime);
        lastTime += 8 * 3600;
        dt = {year(lastTime) - 2000, month(lastTime), day(lastTime), weekday(lastTime), hour(lastTime), minute(lastTime), second(lastTime)};
        Serial2.print("ATO");
    }
    Serial.print("4\t");
    setTime(dt);
    Serial.print("5\t");
}

void setup()
{
    int reason, i;
    Serial.begin(115200);
    Serial2.begin(9600);
    Wire.begin();
    pinMode(E33_Enable, OUTPUT);
    pinMode(LINK, INPUT);
    pinMode(NET, INPUT);
    pinMode(WORK, INPUT);
    pinMode(LED, OUTPUT);
    pinMode(IR, OUTPUT);
    digitalWrite(LED, 0);

    while (!Serial)
        ;
    Serial.println("\n--------begin--------");

    if (!SD.begin())
    {
        Serial.println("Card Mount Failed");
        UPDATE_F = 300;
    }
    else
    {
        Serial.println("TF Card is ready");
        tfStatus = 1;
        if (bootCount == 0)
            getSetting();
    }
    if (connectMode == 'W')
    {
        Serial.println("Link mode:Wifi");
        Serial.printf("MQTT:\n\t-Server:%s\n\t-User:%s\n\t-PassWD:%s\n\t-Port:%d\n", mqtt_server, mqttUser, mqttPassWd, port);
        if (wifiConnect() != 1)
        {
            Serial.println("WiFi connect failed");
            connectMode = 'L';
        }
        else
        {
            initMQTT();
        }
    }
    else if (connectMode == 'L')
    {
        Serial.println("Link mode:Local");
    }
    else
    {
        Serial.println("Link mode:4G");
        E33VStart();
    }
    reason = esp_sleep_get_wakeup_cause();
    Serial.printf("Wakeup reason:%d\tRecord cycle:%d\n", reason, UPDATE_F);

    if (reason == 0) // 第一次启动，初始化
    {
        resetRX8010(); // 先复位设置
        if (connectMode == 'W' && WiFi.status() == WL_CONNECTED)
        {
            dt = timeSync();
            setTime(dt);
            Serial.printf("Set RX8010 new time:%d/%d/%d,%d:%d:%d\n", dt.year, dt.month, dt.date, dt.hour, dt.minute, dt.second);
        }
        sdInit();
        setFCT(IRQ1, UPDATE_F, 2); // 设置5分钟唤醒一次
    }
    dt = getDateTime();
    readAM2120(&humidity, &temp);
    vol = (double)analogRead(ADC) / 566.75;
    if (vol > 4.2)
        vol = 100;
    else if (vol < 3)
        vol = 0;
    else
        vol = (vol - 3) * 100 / 1.2;

    sprintf(msg, "{\"time\":\"%02d-%02d-%02d,%02d:%02d:%02d\",\"temp\":%.1f,\"humi\":%.1f,\"vol\":%.1f,\"cycle\":%d,\"upTimes\":%d}",
            dt.year, dt.month, dt.date, dt.hour, dt.minute, dt.second, temp, humidity, vol, UPDATE_F, bootCount);

    if (connectMode == 'W')
    {
        client.publish("E33V", msg);
    }
    Serial2.println(msg);
    Serial.println("Publish:" + (String)msg);
    tfLogger();
    Serial.println("Boot number: " + String(bootCount));
    ++bootCount; // 打印 ESP32 和触摸板的唤醒原因

    //    attachInterrupt(TF_READY, sdInit, FALLING);
    //  setupAM2120();

    delay(500);
    digitalWrite(LED, 1); // 需要增加一个延时，否则会直接重启
    if (SLEEP_ENABLE)
    {
        // esp_sleep_enable_timer_wakeup(10 * uS_TO_S_FACTOR);
        esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); // 1 = High, 0 = Low
        Serial.println("--------sleep--------\n");
        esp_deep_sleep_start();
    }
}

void loop()
{
    if (!client.connected())
    {
        reconnect();
    }
    client.loop();
}
